<!DOCTYPE html>
<html lang="zh-CN" :data-theme="currentTheme">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>deepseek 调试</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/3.5.13/vue.global.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/marked/5.1.2/marked.min.js"></script>
    <!-- Tailwind CSS -->
    <link href="https://cdn.bootcdn.net/ajax/libs/daisyui/4.12.23/full.min.css" rel="stylesheet" type="text/css" />
    <script src="https://cdn.tailwindcss.com"></script>
    <script>
        tailwind.config = {
            daisyui: {
                themes: [{
                    light: {
                        "primary": "#570DF8",
                        "primary-focus": "#4506CB",
                        "secondary": "#F000B8",
                        "accent": "#37CDBE",
                        "neutral": "#3D4451",
                        "base-100": "#FFFFFF",
                        "base-200": "#F2F2F2",
                        "base-300": "#E5E6E6",
                        "base-content": "#1F2937",
                        "info": "#3ABFF8",
                        "success": "#36D399",
                        "warning": "#FBBD23",
                        "error": "#F87272"
                    },
                    dark: {
                        "primary": "#BB86FC",
                        "primary-focus": "#9965E3",
                        "secondary": "#03DAC6",
                        "accent": "#BB86FC",
                        "neutral": "#121212",
                        "base-100": "#1E1E1E",
                        "base-200": "#2C2C2C",
                        "base-300": "#242424",
                        "base-content": "#E1E1E1",
                        "info": "#0175C2",
                        "success": "#00C853",
                        "warning": "#FFB74D",
                        "error": "#CF6679",
                    }
                }],
            },
        }
    </script>
    <style>
        /* 暗色模式下的滚动条样式 */
        [data-theme='dark'] ::-webkit-scrollbar {
            width: 8px;
            height: 8px;
        }

        [data-theme='dark'] ::-webkit-scrollbar-track {
            background: #2C2C2C;
            border-radius: 4px;
        }

        [data-theme='dark'] ::-webkit-scrollbar-thumb {
            background: #424242;
            border-radius: 4px;
        }

        [data-theme='dark'] ::-webkit-scrollbar-thumb:hover {
            background: #505050;
        }
    </style>
</head>
<body class="h-screen transition-colors duration-200 flex flex-col overflow-hidden">
<div id="app">
    <!-- 导航栏 -->
    <div class="navbar bg-base-100 shadow-lg px-4 flex-none h-16">
        <div class="flex-1">
            <h1 class="text-xl font-bold">Deepseek 调试工具</h1>
        </div>
        <div class="flex-none gap-2">
            <div class="dropdown dropdown-end">
                <label class="swap swap-rotate btn btn-ghost btn-circle">
                    <input type="checkbox"
                           :checked="currentTheme === 'dark'"
                           @change="toggleTheme"
                           class="theme-controller"/>
                    <!-- sun icon -->
                    <svg class="swap-on fill-current w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M5.64,17l-.71.71a1,1,0,0,0,0,1.41,1,1,0,0,0,1.41,0l.71-.71A1,1,0,0,0,5.64,17ZM5,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H4A1,1,0,0,0,5,12Zm7-7a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4A1,1,0,0,0,12,5ZM5.64,7.05a1,1,0,0,0,.7.29,1,1,0,0,0,.71-.29,1,1,0,0,0,0-1.41l-.71-.71A1,1,0,0,0,4.93,6.34Zm12,.29a1,1,0,0,0,.7-.29l.71-.71a1,1,0,1,0-1.41-1.41L17,5.64a1,1,0,0,0,0,1.41A1,1,0,0,0,17.66,7.34ZM21,11H20a1,1,0,0,0,0,2h1a1,1,0,0,0,0-2Zm-9,8a1,1,0,0,0-1,1v1a1,1,0,0,0,2,0V20A1,1,0,0,0,12,19ZM18.36,17A1,1,0,0,0,17,18.36l.71.71a1,1,0,0,0,1.41,0,1,1,0,0,0,0-1.41ZM12,6.5A5.5,5.5,0,1,0,17.5,12,5.51,5.51,0,0,0,12,6.5Zm0,9A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z"/></svg>
                    <!-- moon icon -->
                    <svg class="swap-off fill-current w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M21.64,13a1,1,0,0,0-1.05-.14,8.05,8.05,0,0,1-3.37.73A8.15,8.15,0,0,1,9.08,5.49a8.59,8.59,0,0,1,.25-2A1,1,0,0,0,8,2.36,10.14,10.14,0,1,0,22,14.05,1,1,0,0,0,21.64,13Zm-9.5,6.69A8.14,8.14,0,0,1,7.08,5.22v.27A10.15,10.15,0,0,0,17.22,15.63a9.79,9.79,0,0,0,2.1-.22A8.11,8.11,0,0,1,12.14,19.73Z"/></svg>
                </label>
            </div>
        </div>
    </div>

    <main class="h-[calc(100vh-8rem)] container mx-auto px-4 py-2 overflow-hidden">
        <div class="flex gap-4 h-full">
            <!-- 左侧原始数据区域 -->
            <!-- 新增会话历史侧栏，移到最左侧 -->
            <div class="w-1/4 flex flex-col bg-base-100 dark:bg-base-200 rounded-box shadow-lg dark:shadow-lg/20 p-4">
                <h2 class="text-xl font-bold mb-2 dark:text-base-content/90">会话历史</h2>
                <div class="flex-1 overflow-y-auto">
                    <div
                            v-for="(item, index) in conversationHistory"
                            :key="index"
                            @click="loadConversation(item)"
                            class="p-2 mb-2 rounded hover:bg-base-200 dark:hover:bg-base-300 cursor-pointer"
                    >
                        <div class="text-sm">{{ item.displayTitle }}</div>
                        <div class="text-xs text-gray-500 dark:text-gray-400">
                            {{ item.displayTime }}
                        </div>
                    </div>
                </div>
            </div>
            <div class="w-1/2 flex flex-col">
                <!-- 头部控制区 -->
                <!-- 在头部控制区添加两个按钮选项 -->
                <div class="bg-base-100 dark:bg-base-200 rounded-box shadow-lg dark:shadow-lg/20 p-4 mb-2 flex-none backdrop-blur-sm">
                    <!-- 原有的 API 地址输入框 -->
                    <div class="mb-4 flex items-center">
                        <label class="label w-20">
                            <span class="label-text dark:text-base-content/80">API地址</span>
                        </label>
                        <input
                                type="text"
                                v-model="apiBaseUrl"
                                placeholder="请输入API基础地址"
                                class="input input-bordered w-full dark:bg-base-300 dark:border-base-content/10 dark:text-base-content/90 dark:placeholder-base-content/50"
                        >
                    </div>
                    <!-- 原有的提示词输入框 -->
                    <div class="mb-4 flex items-center">
                        <label class="label w-20">
                            <span class="label-text dark:text-base-content/80">提示词</span>
                        </label>
                        <input
                                type="text"
                                v-model="prompt"
                                placeholder="请输入提示词"
                                class="input input-bordered w-full dark:bg-base-300 dark:border-base-content/10 dark:text-base-content/90 dark:placeholder-base-content/50"
                        >
                    </div>
                    <!-- 新增的两个按钮选项 -->
                    <div class="mb-4 flex items-center">
                        <label class="mr-4">
                            <input type="checkbox" v-model="deepThinking" class="checkbox checkbox-primary">
                            <span class="ml-2">深度思考</span>
                        </label>
                        <label>
                            <input type="checkbox" v-model="onlineSearch" class="checkbox checkbox-primary">
                            <span class="ml-2">联网搜索</span>
                        </label>
                    </div>
                    <div class="flex space-x-2">
                        <button
                                @click="sendMessage"
                                :disabled="isSending || isConnected"
                                :class="{ 'loading': isSending }"
                                class="btn btn-secondary"
                        >
                            {{ isSending ? '发送中...' : '发送' }}
                        </button>
                        <button
                                @click="disconnect"
                                :disabled="!isConnected"
                                class="btn btn-error"
                        >
                            断开
                        </button>
                        <button
                                @click="clearMessages"
                                class="btn btn-ghost"
                        >
                            清空
                        </button>
                         <!-- 新增 WebSocket 语音识别按钮 -->
                         <button
                            @mousedown="startVoiceRecognition"
                            @mouseup="stopVoiceRecognition"
                            @mouseleave="stopVoiceRecognition"
                            :disabled="isVoiceRecognizing"
                            class="btn btn-info"
                        >
                            {{ isVoiceRecognizing ? '识别中...' : '按住说话' }}
                        </button>
                    </div>
                </div>

                <!-- 消息展示区 -->
                <div class="flex-1 flex flex-col min-h-0">
                    <h2 class="text-xl font-bold mb-1 dark:text-base-content/90">原始数据</h2>
                    <div class="flex-1 overflow-y-auto min-h-0 messages-container">
                        <div class="mockup-window bg-base-300 dark:bg-base-300/50 h-full flex flex-col backdrop-blur-sm">
                            <div class="flex-1 px-6 py-4 bg-base-200 dark:bg-base-200/50 overflow-y-auto">
                                <div v-if="messages.length === 0" class="text-gray-400 dark:text-gray-500 text-center py-4">
                                    暂无消息
                                </div>
                                <div
                                        v-for="(message, index) in messages"
                                        :key="index"
                                        class="border-b border-base-200 last:border-0 py-3"
                                >
                                    <div class="text-xs opacity-70 mb-1">{{ message.time }}</div>
                                    <pre class="mockup-code mt-2">{{ message.data }}</pre>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>

            <!-- 右侧答案区域 -->
            <div class="w-1/2 flex flex-col min-h-0">
                <div class="flex-1 flex flex-col min-h-0">
                    <h2 class="text-xl font-bold mb-1">最终答案</h2>
                    <div ref="answerRef" class="flex-1 overflow-y-auto min-h-0">
                        <div class="mockup-window bg-base-300 h-full flex flex-col">
                            <div class="flex-1 px-6 py-4 bg-base-200 overflow-y-auto prose dark:prose-invert max-w-none">
                                <!-- 对话展示区域 -->
                                <div v-for="(item, index) in conversation" :key="index">
                                    <div v-if="item.role === 'user'" class="text-right mb-4">
                                        <div class="bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-100 inline-block p-2 rounded-lg">
                                            {{ item.content }}
                                        </div>
                                    </div>
                                    <div v-else class="text-left mb-4">
                                        <div class="bg-gray-100 dark:bg-gray-900 text-gray-800 dark:text-gray-100 inline-block p-2 rounded-lg">
                                            <div v-html="item.content"></div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>

        </div>
    </main>

    <!-- Footer -->
    <footer class="footer footer-center p-2 bg-base-300 dark:bg-base-300/50 text-base-content dark:text-base-content/70 flex-none h-12 backdrop-blur-sm">
        <aside class="flex items-center gap-2">
            <p>Powered by</p>
            <div class="flex items-center gap-1">
                    <span class="font-semibold">
                        <a href="https://ai.pig4cloud.com" target="_blank">Pig AI</a>
                    </span>
            </div>
        </aside>
    </footer>
</div>

<script>
    const { createApp, ref, computed, watch, nextTick, onMounted } = Vue
    marked.setOptions({
        breaks: true, // 换行符转 <br>
        gfm: true ,// 启用 GitHub Flavored Markdown 模式
        sanitize: true
    })
    createApp({
        setup() {
            // 生成 sessionId 的示例函数
            const generateSessionId = () => {
                return Math.random().toString(36).substr(2, 9);
            }
            const THIRTY_MINUTES = 30 * 60 * 1000;

            
            const deviceIdStorage = localStorage.getItem('deviceId');
            const deviceIdExpire = localStorage.getItem('deviceIdExpire');
            let deviceIdValue;
            if (deviceIdStorage && deviceIdExpire && Date.now() < Number(deviceIdExpire)) {
                deviceIdValue = deviceIdStorage;
            } else {
                deviceIdValue = `web_${generateSessionId()}`;
                localStorage.setItem('deviceId', deviceIdValue);
                localStorage.setItem('deviceIdExpire', Date.now() + THIRTY_MINUTES);
            }
            const deviceId = ref(deviceIdValue);

            // 从 localStorage 中获取 sessionId 及其过期时间
            const sessionIdStorage = localStorage.getItem('sessionId');
            const sessionIdExpire = localStorage.getItem('sessionIdExpire');
            let sessionIdValue;
            if (sessionIdStorage && sessionIdExpire && Date.now() < Number(sessionIdExpire)) {
                sessionIdValue = sessionIdStorage;
            } else {
                sessionIdValue = generateSessionId();
                localStorage.setItem('sessionId', sessionIdValue);
                localStorage.setItem('sessionIdExpire', Date.now() + THIRTY_MINUTES);
            }
            const sessionId = ref(sessionIdValue);


            const apiBaseUrl = ref('http://127.0.0.1:42300/api/ds')
            const prompt = ref('你是谁?')
            const messages = ref([])
            const isConnected = ref(false)
            const currentTheme = ref('light')
            let eventSource = null
            const answerRef = ref(null)
            // 新增的两个状态
            const deepThinking = ref(false)
            const onlineSearch = ref(false)
            const isSending = ref(false) // 新增发送按钮加载状态

            const conversation = ref([]) // 新增对话数组
            // 新增会话历史数组
            const conversationHistory = ref([])

            // 计算完整的SSE URL
            const sseUrl = computed(() => {
                return `${apiBaseUrl.value}/completions`
            })

            // 计算最终答案
            const finalAnswer = computed(() => {
                const rawContent = messages.value
                    .map(m => m.data)
                    .join('')
                return rawContent
            })



            // 切换主题
            const toggleTheme = () => {
                currentTheme.value = currentTheme.value === 'light' ? 'dark' : 'light'
                localStorage.setItem('theme', currentTheme.value)
                document.documentElement.setAttribute('data-theme', currentTheme.value)
            }

            // 滚动到底部的函数
            const scrollToBottom = (element) => {
                if (element) {
                    const scrollContainer = element.querySelector('.overflow-y-auto')
                    if (scrollContainer) {
                        scrollContainer.scrollTop = scrollContainer.scrollHeight
                    }
                }
            }

            // 监听消息变化，自动滚动
            watch(() => [messages.value.length, finalAnswer.value], () => {
                nextTick(() => {
                    if (answerRef.value) {
                        scrollToBottom(answerRef.value)
                    }
                    // 滚动原始数据区域
                    const messagesContainer = document.querySelector('.messages-container .overflow-y-auto')
                    if (messagesContainer) {
                        messagesContainer.scrollTop = messagesContainer.scrollHeight
                    }
                })
            }, { deep: true })

            const sendMessage = () => {
                if (isSending.value) return
                isSending.value = true
                connect()
            }
            const connect = () => {
                if (eventSource) {
                    eventSource.close()
                }

                // 清空消息
                //clearMessages()
                //conversation.value = []

                // 添加用户输入到对话
                conversation.value.push({
                    role: 'user',
                    content: prompt.value
                })

                let lastProcessedIndex = 0; // 记录上一次处理的位置
                try {
                    // 创建一个新的 EventSource 用于 POST 请求
                    const xhr = new XMLHttpRequest();
                    xhr.open('POST', sseUrl.value, true);
                    xhr.setRequestHeader('Content-Type', 'application/json');
                    xhr.setRequestHeader('Cache-Control', 'no-cache');
                    xhr.setRequestHeader('Accept', 'text/event-stream');
                    xhr.onreadystatechange = function() {
                        if (xhr.readyState === 3) {
                            isConnected.value = true;
                            const newData = xhr.responseText.slice(lastProcessedIndex); // 只获取新增的数据
                            const lines = newData.split('\n');
                            let assistantContent = ''
                            lines.forEach(line => {

                                if (line.startsWith('data:')) {
                                    try{
                                        //const messageData = JSON.parse(line.slice(5).trim())
                                        const messageData = line.slice(5).trim() ;
                                        // if (messageData.message) {
                                        //     console.log('原始内容:', messageData.message) // 打印原始内容
                                        //     assistantContent += messageData.message
                                        //     messages.value.push({
                                        //         time: new Date().toLocaleTimeString(),
                                        //         data: messageData.message
                                        //     })
                                        // }
                                        console.log('原始内容:', messageData) // 打印原始内容
                                        if (messageData) {
                                            assistantContent += messageData
                                            messages.value.push({
                                                time: new Date().toLocaleTimeString(),
                                                data: messageData
                                            })
                                        }

                                    }
                                    catch(error){
                                        console.error('Error parsing JSON:', error);
                                    }
                                }
                            });
                            if (assistantContent) {
                                if (conversation.value.length === 0 || conversation.value[conversation.value.length - 1].role === 'user') {
                                    conversation.value.push({
                                        role: 'assistant',
                                        content: assistantContent
                                    })
                                } else {
                                    conversation.value[conversation.value.length - 1].content += assistantContent
                                }
                            }
                            lastProcessedIndex = xhr.responseText.length; // 更新上一次处理的位置
                        } else if (xhr.readyState === 4) {
                            isConnected.value = false
                            isSending.value = false // SSE 返回结束，更新发送按钮状态
                        }
                    };
                    xhr.onerror = function(error) {
                        console.error('SSE Error:', error);
                        disconnect();
                    };
                    const requestData = {
                        sessionId: sessionId.value, // 假设需要生成 sessionId 的函数
                        deviceId: deviceId.value, // 假设设备是 web 端，根据实际情况修改
                        message: prompt.value,
                        deepThinking: deepThinking.value,
                        onlineSearch: onlineSearch.value
                    };
                    xhr.send(JSON.stringify(requestData));
                } catch (error) {
                    console.error('Connection Error:', error);
                    disconnect();
                }
            }
            // 加载会话历史的方法
            const fetchConversationHistory = async () => {
                try {
                    const response = await fetch(`${apiBaseUrl.value}/conversationHistory?deviceId=${deviceId.value}`)
                    const data = await response.json()
                    console.log('获取会话历史:', data)
                    if (data.code === 200) { // 假设成功码为 200
                        conversationHistory.value = data.data.map(item => {
                            return {
                                ...item,
                                displayTitle: item.title.length > 7 ? item.title.slice(0, 7) + '...' : item.title,
                                displayTime: new Date(item.createTime).toLocaleString()
                            }
                        })
                    }
                } catch (error) {
                    console.error('获取会话历史失败:', error)
                }
            }



            const disconnect = () => {
                if (eventSource) {
                    eventSource.close();
                    eventSource = null;
                }
                isConnected.value = false;
            }

            const clearMessages = () => {
                messages.value = [];
            }


            // 加载会话历史的方法
            const loadConversation = async (item) => {
                try {
                    const response = await fetch(`${apiBaseUrl.value}/conversation?sessionId=${item.sessionId}`);
                    const data = await response.json();
                    if (data.code === 200 && Array.isArray(data.data)) {
                        // 假设返回的数据结构中有一个 messages 字段存储对话记录
                        conversation.value = data.data;
                    }
                } catch (error) {
                    console.error('加载会话记录失败:', error);
                }
            }
            //wss
            const isVoiceRecognizing = ref(false) // 新增语音识别状态
            let ws = null // WebSocket 实例
            let mediaRecorder = null
            let stream = null
            // 启动语音识别的方法
            const startVoiceRecognition = async ()  => {
                if (isVoiceRecognizing.value) return
                isVoiceRecognizing.value = true
                try {
                    stream = await navigator.mediaDevices.getUserMedia({
                         audio: {
                            sampleRate: 16000, // 示例采样率
                            channelCount: 1,    // 单声道
                            // 可以尝试添加其他约束
                            echoCancellation: true,
                            noiseSuppression: true
                    }})
                    console.log('已成功获取麦克风权限');
                    // 检查音频轨道
                    if (stream.getAudioTracks().length === 0) {
                        console.error('未检测到音频轨道');
                        isVoiceRecognizing.value = false;
                        return;
                    }
                    //mediaRecorder = new MediaRecorder(stream)
                    const wsUrl = `ws://localhost:42300/websocket?deviceId=${deviceId.value}`;
                     // 初始化 WebSocket 连接
                     ws = new WebSocket(wsUrl)
                     ws.binaryType = 'arraybuffer';
                     ws.onopen = () => {
                        console.log('WebSocket 连接已建立')
                        // 初始化 MediaRecorder
                        mediaRecorder = new MediaRecorder(stream)

                        mediaRecorder.ondataavailable = (event) => {
                            console.log('准备发送音频数据，大小:', event.data.size,ws.readyState);
                            if (event.data.size > 0 && ws.readyState === WebSocket.OPEN) {
                                // 发送音频数据到 WebSocket 服务器
                                const chunkSize = 8000; 
                                const fileReader = new FileReader();

                                // console.log('准备发送音频数据，大小:', event.data.size);
                                // ws.send(event.data)
                                // console.log('音频数据已发送');
                                fileReader.onload = () => {
                                    const arrayBuffer = fileReader.result;
                                    let offset = 0;
                                    while (offset < arrayBuffer.byteLength) {
                                        const end = Math.min(offset + chunkSize, arrayBuffer.byteLength);
                                        const chunk = new Uint8Array(arrayBuffer, offset, end - offset);
                                        ws.send(chunk.buffer);
                                        offset = end;
                                    }
                                };
                                fileReader.readAsArrayBuffer(event.data);
                            }
                        }

                        // 开始录制音频
                        mediaRecorder.start(1000)
                        console.log('MediaRecorder 已启动，状态:', mediaRecorder.state);
                        if (mediaRecorder.state !== 'recording') {
                            console.error('MediaRecorder 未处于录制状态');
                        }
                    }

                    ws.onmessage = (event) => {
                        const result = JSON.parse(event.data)
                        console.log('语音识别结果:', result)
                        // 将识别结果填充到提示词输入框
                        prompt.value = result.text
                    }

                    ws.onclose = (event) => {
                        console.log('WebSocket 连接已关闭，关闭代码:', event.code, '关闭原因:', event.reason);
                        isVoiceRecognizing.value = false
                        if (mediaRecorder && mediaRecorder.state !== 'inactive') {
                            mediaRecorder.stop()
                        }
                        if (stream) {
                            stream.getTracks().forEach(track => track.stop())
                        }
                    }

                    ws.onerror = (error) => {
                        console.error('WebSocket 错误:', error)
                        // 打印错误对象的详细信息
                        if (error instanceof Event) {
                            console.log('错误类型:', error.type);
                            console.log('错误目标:', error.target);
                        }
                        isVoiceRecognizing.value = false
                        if (mediaRecorder && mediaRecorder.state !== 'inactive') {
                            mediaRecorder.stop()
                        }
                        if (stream) {
                            stream.getTracks().forEach(track => track.stop())
                        }
                        ws.close()
                    }


                } catch (error) {
                    console.error('获取麦克风权限失败，错误类型:', error.name, '错误信息:', error.message);
                    isVoiceRecognizing.value = false
                }
            }

            // 停止语音识别的方法
            const stopVoiceRecognition = () => {
                if (!isVoiceRecognizing.value) return
                isVoiceRecognizing.value = false

                if (mediaRecorder && mediaRecorder.state !== 'inactive') {
                    mediaRecorder.stop()
                }
                if (stream) {
                    stream.getTracks().forEach(track => track.stop())
                }
                if (ws && ws.readyState === WebSocket.OPEN) {
                    ws.close()
                }
            }


            // 初始化主题
            onMounted(() => {
                const savedTheme = localStorage.getItem('theme')
                currentTheme.value = savedTheme || 'light'
                document.documentElement.setAttribute('data-theme', currentTheme.value)
                fetchConversationHistory()
            })

            return {
                apiBaseUrl,
                prompt,
                messages,
                isConnected,
                currentTheme,
                toggleTheme,
                connect,
                disconnect,
                clearMessages,
                finalAnswer,
                answerRef,
                deepThinking,
                onlineSearch ,
                conversation,
                isSending, // 返回新的响应式数据
                sendMessage ,// 返回新的方法
                conversationHistory ,
                loadConversation,
                isVoiceRecognizing,
                startVoiceRecognition,
                stopVoiceRecognition
            }
        }
    }).mount('#app')
</script>
</body>
</html>